/* @flow */

import {createRoute, isSameRoute, isIncludedRoute} from '../util/route'
import {_Vue} from '../install'

// work around weird flow bug
const toTypes: Array<Function> = [String, Object]
const eventTypes: Array<Function> = [String, Array]

export default {
    name: 'RouterLink',
    props: {
        to: {
            type: toTypes,
            required: true
        },
        tag: {
            type: String,
            default: 'a'
        },
        exact: Boolean,
        append: Boolean,
        replace: Boolean,
        activeClass: String,
        exactActiveClass: String,
        event: {
            type: eventTypes,
            default: 'click'
        }
    },
    render(h: Function) {
        const router = this.$router
        const current = this.$route
        const {location, route, href} = router.resolve(this.to, current, this.append)

        const classes = {}
        const globalActiveClass = router.options.linkActiveClass
        const globalExactActiveClass = router.options.linkExactActiveClass
        // Support global empty active class
        const activeClassFallback = globalActiveClass == null
            ? 'router-link-active'
            : globalActiveClass
        const exactActiveClassFallback = globalExactActiveClass == null
            ? 'router-link-exact-active'
            : globalExactActiveClass
        const activeClass = this.activeClass == null
            ? activeClassFallback
            : this.activeClass
        const exactActiveClass = this.exactActiveClass == null
            ? exactActiveClassFallback
            : this.exactActiveClass
        //为什么location.path存在就必须创建Route？
        //1）两个路由是否需要设置activeClass是通过比较path获得的
        //2）location.path保存的是最原始的路径，而route可能是经过重定向之后的路由信息对象
        const compareTarget = location.path
            ? createRoute(null, location, null, router)
            : route

        classes[exactActiveClass] = isSameRoute(current, compareTarget)
        classes[activeClass] = this.exact
            ? classes[exactActiveClass]
            : isIncludedRoute(current, compareTarget)

        const handler = e => {
            if (guardEvent(e)) {
                if (this.replace) {
                    router.replace(location)
                } else {
                    router.push(location)
                }
            }
        }

        const on = {click: guardEvent}
        if (Array.isArray(this.event)) {
            this.event.forEach(e => {
                on[e] = handler
            })
        } else {
            on[this.event] = handler
        }

        const data: any = {
            class: classes
        }

        if (this.tag === 'a') {
            data.on = on
            data.attrs = {href}
        } else {
            // find the first <a> child and apply listener and href
            // 如果插槽中存在a标签，则仍然会优先使用该a标签作为触发事件的dom元素
            const a = findAnchor(this.$slots.default)
            if (a) {
                // in case the <a> is a static node
                // 如果a标签存在，则其标签上可能已经存在属性，所以需要保留已存在的属性，
                // 并直接替换某些特殊的属性，比如on、attrs.href
                a.isStatic = false
                const extend = _Vue.util.extend
                const aData = a.data = extend({}, a.data)
                //on属性直接替换，所以
                aData.on = on
                const aAttrs = a.data.attrs = extend({}, a.data.attrs)
                aAttrs.href = href
            } else {
                // doesn't have <a> child, apply listener to self
                data.on = on
            }
        }

        return h(this.tag, data, this.$slots.default)
    }
}

function guardEvent(e) {
    // don't redirect with control keys
    if (e.metaKey || e.altKey || e.ctrlKey || e.shiftKey) return
    // don't redirect when preventDefault called
    if (e.defaultPrevented) return
    // don't redirect on right click
    if (e.button !== undefined && e.button !== 0) return
    // don't redirect if `target="_blank"`
    if (e.currentTarget && e.currentTarget.getAttribute) {
        const target = e.currentTarget.getAttribute('target')
        if (/\b_blank\b/i.test(target)) return
    }
    // this may be a Weex event which doesn't have this method
    if (e.preventDefault) {
        e.preventDefault()
    }
    return true
}

function findAnchor(children) {
    if (children) {
        let child
        for (let i = 0; i < children.length; i++) {
            child = children[i]
            if (child.tag === 'a') {
                return child
            }
            if (child.children && (child = findAnchor(child.children))) {
                return child
            }
        }
    }
}
